using System;
using System.Reflection;

namespace NUtils
{
    // This factory object will create any class that derives from the given
    // interface. Just call "Create" with the name of the class. 
    public class Factory<TInterface>
    {
        // The names (not FullNames, just the class name) of all the objects
        // that derive from TInterface that this class can construct through
        // calling create.
        public string[] Names
        {
            get
            {
                System.Collections.ArrayList keys = new System.Collections.ArrayList();
                keys.AddRange(factory.Keys);
                return keys.ToArray(typeof(string)) as string[];
            }
        }

        public TInterface Create(string name)
        {
            if (null == name || !factory.ContainsKey(name))
            {
                throw new System.Exception("This factory does not contain a pattern for '" + name + "'");
            }

            Type type = factory[name];
            object obj = Assembly.GetCallingAssembly().CreateInstance(type.FullName);
            if (null == obj)
            {
                throw new System.Exception("Failed allocating pathfinding algorithm '" + name + "'");
            }

            return (TInterface)obj;
        }

        public Factory()
        {
            factory = new System.Collections.Generic.Dictionary<string, Type>();

            // Name of the abstract class for all the Pathfinding classes.
            Type PatternInterface = typeof(TInterface);

            // Grab all the types in this assemby
            foreach (Type type in Assembly.GetCallingAssembly().GetTypes())
            {
                // Is this even a class? Automatically excludes interfaces,
                // something that isn't obvious to me as a C++ coder.
                if (!type.IsClass)
                {
                    continue;
                }

                // Exclude abstract classes too
                if (type.IsAbstract)
                {
                    continue;
                }

                // If the interface doesn't exist then this class does not
                // derive from TInterface.
                if (null == type.GetInterface(PatternInterface.FullName))
                {
                    continue;
                }

                factory.Add(type.Name, type);
            }
        }

        //
        // Private Data
        //

        static private System.Collections.Generic.Dictionary<string, Type> factory;
    }
}
